package com.coffee.excel.executor;

import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.style.column.SimpleColumnWidthStyleStrategy;
import com.coffee.excel.MochaExcelConstant;
import com.coffee.excel.MochaExcelUtil;
import com.coffee.excel.metadata.MochaExcelField;
import com.coffee.excel.metadata.MochaImportParameter;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;

import java.io.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 整个流程：
 *  1, 导入模板下载：
 *     1， 根据下载 类型；得到下载模板字段信息；[字段序号必须大于0 且不能重复]
 *     2， 直接下载；
 *  2， 导入数据：
 *    1， 根据导入类型 找到 字段信息；
 *    2， 执行导入
 *        1， 解析出 数据 map<Integer, Map<Integer, String>>  key : row, value: 每行数据；
 *        2， 执行导入可以直接使用 map 也可以转换成 对应对象 在写入数据库;
 *    3， 将导入结果 反写到文件系统，提供用户下载查看导入结果；
 *
 * @author: songkui
 * @since: 2023/2/14 10:51
 */
@Slf4j
public class ImportExcelExecutor {

    private MochaImportParameter importParameter;

    public ImportExcelExecutor(MochaImportParameter importParameter){
        this.importParameter = importParameter;
    }


    public void doImportTemplate(){
        List<String> heads = importParameter.getHead();
        EasyExcel.write(importParameter.getOutputStream())
                .registerWriteHandler(new HeadStyleWriteHandler(importParameter.getHeadColorMap()))
                .registerWriteHandler(new SimpleColumnWidthStyleStrategy(20))
                .sheet(importParameter.getTitleName())
                .head(MochaExcelUtil.head(heads, importParameter.getTitleName()))
                .doWrite(new ArrayList<>());

    }

    public void doImport() throws Exception {
        InputStream readInputStream;
        InputStream resultInputStream;
        byte[] bytes;
        if (importParameter.getInputStream() == null){
            bytes = importParameter.getImportBytes();
            readInputStream = new ByteArrayInputStream(bytes);
        }else {
            readInputStream = importParameter.getInputStream();
            bytes = inputToOutputStream(readInputStream).toByteArray();
        }

        Map<Integer, Map<Integer, String>> dataMap = readExcelDataMap(readInputStream, importParameter);
        Map<Integer, String> row = dataMap.get(1); // 第二行 是 标题头
        if (null != row){
            throw new Exception(row.get(0));
        }

        Map<Integer, String> requiredFieldMap = importParameter.getFieldList().stream()
                .filter(f -> Optional.ofNullable(f.getRequiredType()).orElse(Boolean.FALSE))
                .collect(Collectors.toMap(MochaExcelField::getColumnIndex, MochaExcelField::getHeadShow));

        Map<Integer, String> fieldMap = importParameter.getFieldList().stream()
                .collect(Collectors.toMap(MochaExcelField::getColumnIndex, MochaExcelField::getFieldValue));

        Map<Integer, String> resMap = verifyRequested(requiredFieldMap, dataMap); //1 校验必填项目
        Map<Integer, String> resImportMap = importParameter.getMochaImportHandler().doImport(fieldMap, dataMap); //执行导入
        resMap.putAll(resImportMap);

        //write back
        resultInputStream = new ByteArrayInputStream(bytes);
        writeImportResult(resultInputStream, importParameter.getOutputStream(), resMap);
    }

    private  Map<Integer, Map<Integer, String>> readExcelDataMap(InputStream inputStream, MochaImportParameter parameter){
        Map<Integer, Map<Integer, String>> dataMap = new HashMap<>();
        EasyExcel.read(inputStream, new MochaAnalysisDataListener(dataMap, parameter.getHead(), parameter.getTitleName()))
                .sheet()
                .headRowNumber(2)
                .doRead();
        return dataMap;
    }

    private void writeImportResult(InputStream inputStream, OutputStream outputStream, Map<Integer, String> resultMap){
        //1.创建一个工作簿
        Workbook workbook;
        try {
            workbook = WorkbookFactory.create(inputStream);
            Sheet sheet = workbook.getSheetAt(0); //获取工作表
            Set<Integer> keys = resultMap.keySet();
            Cell cell;
            for (Integer rowIndex : keys){
                cell = sheet.getRow(rowIndex).getCell(0);
                cell = cell == null ? sheet.getRow(rowIndex).createCell(0) : cell;
                boolean isSuccess = MochaExcelConstant.IMPORT_RESULT_SUCCESS.equals(resultMap.get(rowIndex)) ? true : false;
                cell.setCellStyle(MochaExcelUtil.buildResultStyle(workbook, isSuccess));
                cell.setCellValue(resultMap.get(rowIndex));
            }
            //写入
            workbook.write(outputStream);
            inputStream.close();
        } catch (Exception e) {
            log.error("写入导入结果异常：", e);
        }finally {
            try {
                if (inputStream != null){
                    inputStream.close();
                }

            } catch (IOException e) {
                log.error("写入导入结果-关闭流异常：", e);
            }
        }

    }

    private ByteArrayOutputStream inputToOutputStream(InputStream input) {
        ByteArrayOutputStream outputStream = null;
        try {
            outputStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[4096];
            int len;
            while ((len = input.read(buffer)) > -1) {
                outputStream.write(buffer, 0, len);
            }

            outputStream.flush();
            outputStream.close();

            return outputStream;
        } catch (IOException e) {
             log.error("clone stream exception:", e);
        }finally {
            try {
                if (null != outputStream){
                    outputStream.flush();
                    outputStream.close();
                }
            } catch (IOException e) {
                log.error("clone stream exception-close:", e);
            }
            return outputStream;
        }
    }

    private Map<Integer, String> verifyRequested(Map<Integer, String> requiredFieldMap, Map<Integer, Map<Integer, String>> dataMap){
        Map<Integer, String> resultMap = new HashMap<>();

        Set<Map.Entry<Integer, Map<Integer, String>>> dataSet = dataMap.entrySet();
        Iterator<Map.Entry<Integer, Map<Integer, String>>> iterator = dataSet.iterator();

        while (iterator.hasNext()){
            Map.Entry<Integer, Map<Integer, String>> entry = iterator.next();
            String vqf = MochaExcelUtil.verifyRequiredField(requiredFieldMap, entry.getValue());
            if (StrUtil.isNotEmpty(vqf)){
                resultMap.put(entry.getKey(), "导入失败:  \r\n"+vqf);
                iterator.remove();
            }
        }

        return resultMap;
    }





}
